home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / SAT 2.3.7 / Add-ons / Sprite behavior / SATToolbox.p < prev   
Encoding:
Text File  |  1995-08-21  |  19.6 KB  |  550 lines  |  [TEXT/PJMM]

  1. KeepOnScreen(theSprite) then
  2.             ;
  3.     end; (*MoveSprite*)
  4.  
  5. {$ifc _hasfixedpoint}
  6.     procedure MoveSpriteFixedPoint (theSprite: FixSpritePtr);
  7.     begin
  8.         theSprite^.fixedPointPosition.h := theSprite^.fixedPointPosition.h + theSprite^.speed.h;
  9.         theSprite^.fixedPointPosition.v := theSprite^.fixedPointPosition.v + theSprite^.speed.v;
  10.         theSprite^.position.h := BSR(theSprite^.fixedPointPosition.h, kFixedPointShift);
  11.         theSprite^.position.v := BSR(theSprite^.fixedPointPosition.v, kFixedPointShift);
  12.         if KeepOnScreenFixed(theSprite) then
  13.             ;
  14.     end; (*MoveSpriteFixedPoint*)
  15. {$ENDC}
  16.  
  17.  
  18. (* KeepOnScreen makes border checks to keep the sprite within the window.}
  19. {on a border hit, the speed is negated in order to make the sprite bounce.}
  20. {KeepOnScreen returns true if a border was hit. *)
  21.  
  22.     function KeepOnScreen (theSprite: SpritePtr): Boolean;
  23.         var
  24.             returnValue: Boolean;
  25.     begin
  26.         returnValue := false;
  27.         if theSprite^.position.h < 0 then
  28.             begin
  29.                 theSprite^.position.h := 0;
  30.                 theSprite^.speed.h := abs(theSprite^.speed.h);
  31.                 returnValue := true;
  32.             end;
  33.         if theSprite^.position.v < 0 then
  34.             begin
  35.                 theSprite^.position.v := 0;
  36.                 theSprite^.speed.v := abs(theSprite^.speed.v);
  37.                 returnValue := true;
  38.             end;
  39.         if theSprite^.position.h > gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right then
  40.             begin
  41.                 theSprite^.position.h := gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right;
  42.                 theSprite^.speed.h := -abs(theSprite^.speed.h);
  43.                 returnValue := true;
  44.             end;
  45.         if theSprite^.position.v > gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom then
  46.             begin
  47.                 theSprite^.position.v := gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom;
  48.                 theSprite^.speed.v := -abs(theSprite^.speed.v);
  49.                 returnValue := true;
  50.             end;
  51.  
  52.         KeepOnScreen := returnValue;
  53.     end; (*KeepOnScreen*)
  54.  
  55.  
  56. {$ifc _hasfixedpoint}
  57. (*Same as above, but also modifies the fixedPointPosition field*)
  58.     function KeepOnScreenFixed (theSprite: FixSpritePtr): Boolean;
  59.         var
  60.             returnValue: Boolean;
  61.     begin
  62.         returnValue := false;
  63.         if theSprite^.fixedPointPosition.h < 0 then
  64.             begin
  65.                 theSprite^.position.h := 0;
  66.                 theSprite^.fixedPointPosition.h := 0;
  67.                 theSprite^.speed.h := abs(theSprite^.speed.h);
  68.                 returnValue := true;
  69.             end;
  70.         if theSprite^.fixedPointPosition.v < 0 then
  71.             begin
  72.                 theSprite^.position.v := 0;
  73.                 theSprite^.fixedPointPosition.v := 0;
  74.                 theSprite^.speed.v := abs(theSprite^.speed.v);
  75.                 returnValue := true;
  76.             end;
  77.         if theSprite^.position.h > gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right then
  78.             begin
  79.                 theSprite^.position.h := gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right;
  80.                 theSprite^.fixedPointPosition.h := BSL(theSprite^.position.h, kFixedPointShift);
  81.                 theSprite^.speed.h := -abs(theSprite^.speed.h);
  82.                 returnValue := true;
  83.             end;
  84.         if theSprite^.position.v > gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom then
  85.             begin
  86.                 theSprite^.position.v := gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom;
  87.                 theSprite^.fixedPointPosition.v := BSL(theSprite^.position.v, kFixedPointShift);
  88.                 theSprite^.speed.v := -abs(theSprite^.speed.v);
  89.                 returnValue := true;
  90.             end;
  91.  
  92.         KeepOnScreenFixed := returnValue
  93.     end; (*KeepOnScreenFixed*)
  94. {$endif}
  95.  
  96.  
  97. (* Moves two sprites apart, to separate them with respect to their bounding boxes. *)
  98.  
  99.     function RectSeparate (theSprite: SpritePtr; anotherSprite: SpritePtr; push: Integer): Integer;
  100.         var
  101.             distance: array[0..3] of Integer;
  102.             shortest, shortestDistance, i: Integer;
  103.             bounds1, bounds2: Rect;
  104.             pushMe, pushHim: Integer;
  105.     begin
  106.         bounds1 := theSprite^.hotRect;    {Duger inte hotRect2???}
  107.         OffsetRect(bounds1, theSprite^.position.h, theSprite^.position.v);
  108.  
  109.         bounds2 := anotherSprite^.hotRect;        {Duger inte hotRect2???}
  110.         OffsetRect(bounds2, anotherSprite^.position.h, anotherSprite^.position.v);
  111.  
  112. (*Calculate the distance to separate the sprites in every direction*)
  113.         distance[0] := bounds2.top - bounds1.bottom; {up}
  114.         distance[1] := bounds2.bottom - bounds1.top; {down}
  115.         distance[2] := bounds2.right - bounds1.left; {right}
  116.         distance[3] := bounds2.left - bounds1.right; {left}
  117.  
  118. (*Find the shortest distance*)
  119.         shortest := 0;
  120.         shortestDistance := abs(distance[0]);
  121.         for i := 1 to 3 do
  122.             if abs(distance[i]) < shortestDistance then
  123.                 begin
  124.                     shortest := i;
  125.                     shortestDistance := abs(distance[i]);
  126.                 end;
  127.  
  128. (*Move the sprite in the appropriate direction*)
  129.         case push of
  130.             kPushBoth: 
  131.                 begin
  132.                     pushMe := BSR(distance[shortest], 1);
  133.                     pushHim := distance[shortest] - pushMe;
  134.                     case shortest of
  135.                         0, 1: 
  136.                             begin
  137.                                 theSprite^.position.v := theSprite^.position.v + pushMe;
  138.                                 anotherSprite^.position.v := anotherSprite^.position.v - pushHim;
  139.                             end;
  140.                         2, 3: 
  141.                             begin
  142.                                 theSprite^.position.h := theSprite^.position.h + pushMe;
  143.                                 anotherSprite^.position.h := anotherSprite^.position.h - pushHim;
  144.                             end;
  145.                     end; {case}
  146.                 end;
  147.             kPushMe: 
  148.                 case shortest of
  149.                     0, 1: 
  150.                         theSprite^.position.v := theSprite^.position.v + distance[shortest];
  151.                     2, 3: 
  152.                         theSprite^.position.h := theSprite^.position.h + distance[shortest];
  153.                 end; {case}
  154.             kPushHim: 
  155.                 case shortest of
  156.                     0, 1: 
  157.                         anotherSprite^.position.v := anotherSprite^.position.v - distance[shortest];
  158.                     2, 3: 
  159.                         anotherSprite^.position.h := anotherSprite^.position.h - distance[shortest];
  160.                 end; {case}
  161.         end; {case}
  162.         RectSeparate := shortest;
  163.     end; (*RectSeparate*)
  164.  
  165.     procedure InternalDoBounce (me, him: SpritePtr; separateResult, push: Integer);
  166.         var
  167.             tempSpeed: integer;
  168.     begin
  169.         case push of
  170.             kPushBoth: 
  171.                 if separateResult >= 2 then { 2 or 3: horizontal, otherwise vertical}
  172.                     begin
  173.                         tempSpeed := me^.speed.h;
  174.                         me^.speed.h := him^.speed.h;
  175.                         him^.speed.h := tempSpeed;
  176.                     end
  177.                 else
  178.                     begin
  179.                         tempSpeed := me^.speed.v;
  180.                         me^.speed.v := him^.speed.v;
  181.                         him^.speed.v := tempSpeed;
  182.                     end;
  183.             kPushMe: 
  184.                 if separateResult >= 2 then
  185.                     begin
  186.                         if me^.position.h > him^.position.h then
  187.                             me^.speed.h := Abs(me^.speed.h)
  188.                         else
  189.                             me^.speed.h := -Abs(me^.speed.h);
  190.                     end
  191.                 else
  192.                     begin
  193.                         if me^.position.v > him^.position.v then
  194.                             me^.speed.v := Abs(me^.speed.v)
  195.                         else
  196.                             me^.speed.v := -Abs(me^.speed.v);
  197.                     end;
  198.             kPushHim: 
  199.                 if separateResult >= 2 then { 2 or 3: horizontal, otherwise vertical}
  200.                     begin
  201.                         if him^.position.h > me^.position.h then
  202.                             him^.speed.h := Abs(him^.speed.h)
  203.                         else
  204.                             him^.speed.h := -Abs(him^.speed.h);
  205.                     end
  206.                 else
  207.                     begin
  208.                         if him^.position.v > me^.position.v then
  209.                             him^.speed.v := Abs(him^.speed.v)
  210.                         else
  211.                             him^.speed.v := -Abs(him^.speed.v);
  212.                     end;
  213.         end; {case}
  214.     end; {InternalDoBounce}
  215.  
  216.     procedure RectBounce (me, him: SpritePtr; push: Integer);
  217.         var
  218.             result: Integer;
  219.     begin
  220.         result := RectSeparate(me, him, push);
  221.         InternalDoBounce(me, him, result, push);
  222.     end; {RectBounce}
  223.  
  224.     procedure RectBounceFixed (me, him: FixSpritePtr; push: Integer);
  225.         var
  226.             result: integer;
  227.     begin
  228.         result := RectSeparate(SpritePtr(me), SpritePtr(him), push);
  229.         InternalDoBounce(SpritePtr(me), SpritePtr(him), result, push);
  230.  
  231.         me^.fixedPointPosition.h := BSL(me^.position.h, kFixedPointShift);
  232.         me^.fixedPointPosition.v := BSL(me^.position.v, kFixedPointShift);
  233.         him^.fixedPointPosition.h := BSL(him^.position.h, kFixedPointShift);
  234.         him^.fixedPointPosition.v := BSL(him^.position.v, kFixedPointShift);
  235.     end; {RectBounceFixed}
  236.  
  237.  
  238. {Check if a point is within a sprite – useful for mouse hits in sprites}
  239.  
  240.     function PtInSpriteRgn (p: Point; theSprite: SpritePtr): Boolean;
  241.         var
  242.             faceRgn: RgnHandle;
  243.     begin
  244.         PtInSpriteRgn := false;
  245.         if theSprite = nil then
  246.             Exit(PtInSpriteRgn);
  247.         if theSprite^.face = nil then
  248.             Exit(PtInSpriteRgn);
  249.  
  250.         faceRgn := NewRgn;
  251.  
  252.         CopyRgn(theSprite^.face^.maskRgn, faceRgn);
  253.         OffsetRgn(faceRgn, theSprite^.position.h, theSprite^.position.v);
  254.  
  255.         PtInSpriteRgn := PtInRgn(p, faceRgn);
  256.         DisposeRgn(faceRgn);
  257.     end; {PtInSpriteRgn}
  258.  
  259.  
  260. (* Collision test using regions! *)
  261.  
  262.     function RegionHit (theSprite: SpritePtr; anotherSprite: SpritePtr): Boolean;
  263.         var
  264.             faceRegion1, faceRegion2: RgnHandle;
  265.             result: Boolean;
  266.     begin
  267.         faceRegion1 := NewRgn;
  268.         faceRegion2 := NewRgn;
  269.  
  270.         CopyRgn(theSprite^.face^.maskRgn, faceRegion1);
  271.         OffsetRgn(faceRegion1, theSprite^.position.h, theSprite^.position.v);
  272.  
  273.         CopyRgn(anotherSprite^.face^.maskRgn, faceRegion2);
  274.         OffsetRgn(faceRegion2, anotherSprite^.position.h, anotherSprite^.position.v);
  275.  
  276.         SectRgn(faceRegion1, faceRegion2, faceRegion1);
  277.         result := not EmptyRgn(faceRegion1);
  278.  
  279.         DisposeRgn(faceRegion1);
  280.         DisposeRgn(faceRegion2);
  281.  
  282.         RegionHit := result;
  283.     end; (*RegionHit*)
  284.  
  285.  
  286. (* Split a vector v into a component p parallel to another vector d,}
  287. {and a component n that is perpendicular to d. Useful for realistic}
  288. {collision handling! *)
  289.  
  290.     procedure SplitVector (v: Point; d: Point; var p: Point; var n: Point);
  291.         var
  292.             length2, dotProduct: LongInt;
  293.     begin
  294.         length2 := d.h * d.h + d.v * d.v;    (*Squared length of "d"*)
  295.  
  296.         dotProduct := v.h * d.h + v.v * d.v;    (*Scalar product*)
  297.  
  298.         p.h := d.h * dotProduct div length2;
  299.         p.v := d.v * dotProduct div length2;
  300.         n.h := v.h - p.h;
  301.         n.v := v.v - p.v;
  302.     end; (* SplitVector *)
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.     procedure RegionSeparate (i, j: FixSpritePtr; r1, r2, diff: RgnHandle);
  317.         var
  318.             internalDiff: RgnHandle;
  319.             initVector, nowVector: Point;
  320.             absH, absV: integer;
  321.             moveH, moveV: integer;
  322.             frac: integer;
  323. {Normal signum function (which I don't think is in the libs)}
  324.         function Sgn (x: integer): integer;
  325.         begin
  326.             if x > 0 then
  327.                 Sgn := 1
  328.             else if x < 0 then
  329.                 Sgn := -1
  330.             else
  331.                 Sgn := 0;
  332.         end;
  333.  
  334.     begin {RegionSeparate}
  335.         internalDiff := NewRgn;
  336.         frac := 0;
  337.         initVector.h := i^.position.h - j^.position.h;
  338.         initVector.v := i^.position.v - j^.position.v;
  339.  
  340.         initVector.h := -(diff^^.rgnBBox.right + diff^^.rgnBBox.left) div 2 + (r1^^.rgnBBox.right + r1^^.rgnBBox.left) div 2;
  341.         initVector.v := -(diff^^.rgnBBox.bottom + diff^^.rgnBBox.top) div 2 + (r1^^.rgnBBox.bottom + r1^^.rgnBBox.top) div 2;
  342.  
  343.         absH := abs(initVector.h);
  344.         absV := abs(initVector.v);
  345.         moveH := Sgn(initVector.h);
  346.         moveV := Sgn(initVector.v);
  347.         if moveH = 0 then
  348.             if moveV = 0 then
  349.                 moveV := 1;
  350.         repeat
  351.             if absH > absV then
  352.                 begin
  353.                     i^.position.h := i^.position.h + moveH;
  354.                     OffsetRgn(r1, moveH, 0);
  355. {j^.position.h := j^.position.h - moveH;}
  356.                     frac := frac + absV;
  357.                     if frac > absH then
  358.                         begin
  359.                             i^.position.v := i^.position.v + moveV;
  360.                             OffsetRgn(r1, 0, moveV);
  361. {j^.position.v := j^.position.v - moveV;}
  362.                             frac := frac - absH;
  363.                         end
  364.                 end
  365.             else
  366.                 begin
  367.                     i^.position.v := i^.position.v + moveV;
  368.                     OffsetRgn(r1, 0, moveV);
  369. {j^.position.v := j^.position.v - moveV;}
  370.                     frac := frac + absH;
  371.                     if frac > absV then
  372.                         begin
  373.                             i^.position.h := i^.position.h + moveH;
  374.                             OffsetRgn(r1, moveH, 0);
  375. {j^.position.h := j^.position.h - moveH;}
  376.                             frac := frac - absV;
  377.                         end
  378.                 end;
  379.             nowVector.h := i^.position.h - j^.position.h;
  380.             nowVector.v := i^.position.v - j^.position.v;
  381.  
  382.             SectRgn(r1, r2, internalDiff);
  383.         until EmptyRgn(internalDiff);
  384.  
  385. {until longint(nowVector.h) * nowVector.h + longint(nowVector.v) * nowVector.v > kBallDiameterSquared;}
  386.         i^.fixedPointPosition.h := BSL(i^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
  387.         i^.fixedPointPosition.v := BSL(i^.position.v, 4);
  388.  
  389. {j^.fixedPos.h := BSL(j^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
  390. {j^.fixedPos.v := BSL(j^.position.v, 4);}
  391.  
  392.         DisposeRgn(internalDiff);
  393.     end;{RegionSeparate}
  394.  
  395.  
  396.  
  397. {Variant av RegionHitTest som antar att s1 är en flyttbar sprite (boll) och den andra}
  398. {ett icke cirkulärt objekt.}
  399.  
  400.     procedure RegionBounce (s1, s2: FixSpritePtr);
  401.         var
  402.             r1, r2, diff: RgnHandle;
  403.             vector, parallel, normal: Point;
  404.     begin
  405.  
  406.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  407.             begin
  408.                 SATReportStr('Error: No mask region!');
  409.                 exit(RegionBounce);
  410.             end;
  411.  
  412. {Make copies of the mask regions and offset them to the proper places.}
  413.         r1 := NewRgn;
  414.         r2 := NewRgn;
  415.         diff := NewRgn;
  416.         CopyRgn(s1^.face^.maskRgn, r1);
  417.         CopyRgn(s2^.face^.maskRgn, r2);
  418.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  419.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  420.  
  421.         SectRgn(r1, r2, diff);                    {Is there any overlap?}
  422.  
  423. {If empty, no collision, otherwise, handle the collision!}
  424.         if not EmptyRgn(diff) then
  425.             begin
  426.                 vector.h := (diff^^.rgnBBox.right + diff^^.rgnBBox.left) div 2 - (r1^^.rgnBBox.right + r1^^.rgnBBox.left) div 2;
  427.                 vector.v := (diff^^.rgnBBox.bottom + diff^^.rgnBBox.top) div 2 - (r1^^.rgnBBox.bottom + r1^^.rgnBBox.top) div 2;
  428.  
  429.                 RegionSeparate(s1, s2, r1, r2, diff);
  430.  
  431. {Avstuds som antar att vi skall ha elastisk studs utan störningar.}
  432. {Flipprar, bumpers och sånt måste ha diverse påslag och variationer.}
  433. {För att få plastisk studs - det vill man också ha ibland - så skall väl parallell-vektorn tas bort}
  434. {eller dämpas kraftigt? Brus?}
  435.                 if Longint(vector) <> 0 then {Skydd mot div med 0.}
  436.                     begin
  437.                         SplitVector(s1^.speed, vector, parallel, normal);
  438.                         s1^.speed.h := -parallel.h div 2 + normal.h;
  439.                         s1^.speed.v := -parallel.v div 2 + normal.v;
  440.                     end;
  441.  
  442.             end;
  443.  
  444.         DisposeRgn(r1);
  445.         DisposeRgn(r2);
  446.         DisposeRgn(diff);
  447.     end; {RegionBounce}
  448.  
  449.  
  450.  
  451.  
  452.  
  453. {Look-up table handlers:}
  454.  
  455.     const
  456.         kMaxAngle = 360;
  457.         kMaxSqrt = 1000;
  458.         kPi = 3.1416;
  459.     var
  460.         sinTable: array[0..kMaxAngle] of Longint;
  461.         sqrtTable: array[0..kMaxSqrt] of Longint;
  462.  
  463.     procedure InitTables;
  464.         var
  465.             i: Longint;
  466.     begin
  467.         for i := 0 to kMaxSqrt do
  468.             sqrtTable[i] := Trunc(sqrt(kFixedOne * i));
  469.         for i := 0 to kMaxAngle do
  470.             sinTable[i] := Trunc(kFixedOne * sin(i * kPi / kMaxAngle));
  471.     end; {InitTables}
  472.  
  473. {Take the fixed-point square root of a fixed-point number}
  474.     function SquareRoot (arg: Longint): Longint;
  475.         var
  476.             shift: Integer;
  477.     begin
  478.         if sqrtTable[kFixedOne] <> kFixedOne then
  479.             InitTables;
  480.         shift := 0;
  481.         if arg < 0 then
  482.             begin
  483.                 SquareRoot := 0;
  484.                 Exit(SquareRoot);
  485.             end;
  486.         while arg > kMaxSqrt do
  487.             begin
  488.                 shift := shift + 1;
  489.                 arg := BSR(arg, 2);
  490.             end;
  491.         SquareRoot := BSL(sqrtTable[arg], shift);
  492.     end; {SquareRoot}
  493.  
  494. {Get the fixed-point sinus of a fixed-point number}
  495.     function Sinus (arg: Longint): Longint;
  496.     begin
  497.         if sqrtTable[kFixedOne] <> kFixedOne then
  498.             InitTables;
  499.         while arg < 0 do
  500.             arg := arg + kMaxAngle;
  501.         while arg > kMaxAngle do
  502.             arg := arg - kMaxAngle;
  503.         Sinus := sinTable[arg];
  504.     end; {Sinus}
  505.  
  506. {Get the fixed-point cosinus of a fixed-point number}
  507.     function Cosinus (arg: Longint): Longint;
  508.     begin
  509.         if sqrtTable[kFixedOne] <> kFixedOne then
  510.             InitTables;
  511.         arg := arg + BSR(kMaxAngle, 2); {Displace sinus to get cosinus!}
  512.         while arg < 0 do
  513.             arg := arg + kMaxAngle;
  514.         while arg > kMaxAngle do
  515.             arg := arg - kMaxAngle;
  516.         Cosinus := sinTable[arg];
  517.     end; {Cosinus}
  518.  
  519. {Get the length of a vector (integer vector, fixed-point result!)}
  520.     function VectorLength (vector: Point): Longint;
  521.     begin
  522.         VectorLength := SquareRoot(BSL(vector.h * vector.h + vector.v * vector.v, kFixedPointShift));
  523.     end; {VectorLength}
  524.  
  525. {Get the length of a vector (fixed-point vector, fixed-point result!)}
  526.     function FPVectorLength (vector: Point): Longint;
  527.     begin
  528.         FPVectorLength := SquareRoot(vector.h * vector.h + vector.v * vector.v);
  529.     end; {VectorLength}
  530.  
  531. {Lägg till ApproxVectorLength, den där approximationen som föreslogs, med 3/8.}
  532. {Var det maj + min *3/8? KOLLA!}
  533.     function ApproxVectorLength (vector: Point): Longint;
  534.         var
  535.             maj, min: Integer;
  536.     begin
  537.         if abs(vector.h) > abs(vector.v) then
  538.             begin
  539.                 maj := abs(vector.h);
  540.                 min := abs(vector.v);
  541.             end
  542.         else
  543.             begin
  544.                 maj := abs(vector.v);
  545.                 min := abs(vector.h);
  546.             end;
  547.         ApproxVectorLength := Longint(maj) + BSR(min + BSR(min, 1), 2); {STÄMMER DEN?}
  548.     end; {ApproxVectorLength}
  549.  
  550. end.